#include "carddetectdlg.h"
#include "ui_carddetectdlg.h"
#include <QMessageBox>
#include <qthread.h>
#include <QFileDialog>

CardDetectDlg::CardDetectDlg(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::CardDetectDlg)
{
    ui->setupUi(this);
    setWindowFlags(Qt::Window /*| Qt::WindowStaysOnTopHint*/);
    setAttribute(Qt::WA_DeleteOnClose); // 确保窗口关闭时对象销毁
    setWindowTitle("证件扫描");

    // 1. 主线程创建 detector（此时未初始化 QProcess）
    m_detector = std::make_unique<CvUtils::cardCornerDetector>();
    // 2. 创建工作线程，将 detector move 到工作线程
    m_detectThread = std::make_unique<DetectorThread>(m_detector.get());

    // 3. 连接工作线程启动信号：先初始化 Python 进程，再加载模型（均在工作线程执行）
    connect(m_detectThread->thread(), &QThread::started, m_detector.get(),
            &CvUtils::cardCornerDetector::initPythonProcess, Qt::QueuedConnection);


    connect(m_detector.get(), &CvUtils::cardCornerDetector::sig_initFinished, this, [this]() {
        qDebug() << "[CardDetectDlg] Python 进程初始化完成，开始加载模型";
        emit m_detector->sig_loadModel("D:/Qt_Projects/GreatScanner/keypoint_detection/model/best.pt");
    }, Qt::QueuedConnection);

    //功能信号槽:
    connect(m_detector.get(), &CvUtils::cardCornerDetector::sig_modelLoaded,
            this, &CardDetectDlg::modelLoaded,
            Qt::QueuedConnection);  // 显式指定队列连接

    connect(m_detector.get(), &CvUtils::cardCornerDetector::sig_errorOccurred,
            this, &CardDetectDlg::errorOccurred,
            Qt::QueuedConnection);

    connect(m_detector.get(), &CvUtils::cardCornerDetector::sig_detectionFinished,
            this, &CardDetectDlg::process,
            Qt::QueuedConnection);

    connect(m_detector.get(), &CvUtils::cardCornerDetector::sig_detectionVisualized,
            this, &CardDetectDlg::visualize,
            Qt::QueuedConnection);
}

CardDetectDlg::~CardDetectDlg()
{
    // 关键步骤1：安全处理退出信号（仅当检测器和线程有效时）
    if (m_detector && m_detectThread && m_detectThread->thread()->isRunning()) {
        QEventLoop exitLoop; // 异步等待循环
        bool exitFinished = false;

        // 连接“退出完成”信号，退出等待循环
        QMetaObject::Connection conn = QObject::connect(
            m_detector.get(), &CvUtils::cardCornerDetector::sig_exitFinished,this,
            [&]() {
                exitFinished = true;
                exitLoop.quit(); // 退出等待
            },
            Qt::QueuedConnection
            );

        // 发射退出信号（工作线程处理）
        emit m_detector->sig_exitProcess();

        // 等待退出完成（最多5秒超时）
        QTimer::singleShot(5000, &exitLoop, &QEventLoop::quit); // 超时保护
        exitLoop.exec(); // 阻塞到信号触发或超时

        // 断开连接，避免悬空
        disconnect(conn);

        qDebug() << "[~CardDetectDlg] 退出信号处理完成（是否超时:" << !exitFinished << "）";
    }

    // 关键步骤2：停止工作线程（此时退出逻辑已处理完）
    if (m_detectThread && m_detectThread->thread()->isRunning()) {
        m_detectThread->thread()->quit();
        // 等待线程安全退出（最多3秒）
        if (!m_detectThread->thread()->wait(3000)) {
            qWarning() << "[~CardDetectDlg] 线程超时，强制终止";
            m_detectThread->thread()->terminate();
            m_detectThread->thread()->wait(1000);
        }
    }

    // 关键步骤3：释放检测器（让其在归属线程析构）
    if (m_detector) {
        // 先断开所有与m_detector的连接，避免信号触发已析构对象
        m_detector->disconnect();
        m_detector.release()->deleteLater();
    }

    // 释放UI
    delete ui;
}

//传入证件正反面图片
void CardDetectDlg::showScanRes(const char *frontPath, const char *backPath)
{
    // 保存路径并标记“路径就绪”
    m_frontPath = QString::fromLocal8Bit(frontPath);
    m_backPath = QString::fromLocal8Bit(backPath);

    // 验证图片文件是否存在
    if (!QFile::exists(m_frontPath)) {
        QMessageBox::warning(this, tr("文件错误"), tr("证件正面图片不存在：%1").arg(m_frontPath));
        m_pathsReady = false; // 路径无效，标记为未就绪
        return;
    }
    if (!QFile::exists(m_backPath)) {
        QMessageBox::warning(this, tr("文件错误"), tr("证件反面图片不存在：%1").arg(m_backPath));
        m_pathsReady = false; // 路径无效，标记为未就绪
        return;
    }

    // 路径有效，标记为就绪
    m_pathsReady = true;
    qDebug() << "[showScanRes] 图片路径已保存，触发检测条件校验";

    // 关键修改：调用统一触发函数，校验模型是否已加载
    triggerDetection();
}

void CardDetectDlg::modelLoaded(bool success, const QString &message)
{
    qDebug() << "[modelLoaded] 模型加载结果：" << success;
    if (success) {
        m_isModelLoaded = true;
        qDebug()<<"模型加载成功:"<<message;
    } else {
        m_isModelLoaded = false;
        QMessageBox::critical(this, "模型加载失败", message);
    }
}

void CardDetectDlg::errorOccurred(const QString &errorMsg)
{
    QMessageBox::critical(this, "操作错误", errorMsg);
    // close(); // 关闭窗口
}

//生成垂直排版(如身份证复印件版面)的图片(白色背景), 并显示预览m_preview
void CardDetectDlg::process(const std::vector<CardDetectionResult>& fresults,
                            const std::vector<CardDetectionResult>& bresults)
{
    //由于接口设计的是可以兼容多张证件但是懒得开发了，这里严格要求正反面图片中都必须只有一张证件
    // 检查是否检测到正面证件
    if (fresults.empty()) {
        QMessageBox::warning(this, "检测失败", "未检测到正面证件");
        return;
    }

    // 检查是否检测到反面证件
    if (bresults.empty()) {
        QMessageBox::warning(this, "检测失败", "未检测到反面证件");
        return;
    }

    // 方案一：单证件场景下，取向量第一个元素
    const CardDetectionResult& frontResult = fresults[0];
    const CardDetectionResult& backResult = bresults[0];

    // 验证角点数量是否正确
    if (frontResult.corners.size() != 4) {
        QMessageBox::warning(this, "检测异常", "正面证件角点数量不正确");
        return;
    }

    if (backResult.corners.size() != 4) {
        QMessageBox::warning(this, "检测异常", "反面证件角点数量不正确");
        return;
    }

    // 对正反面证件进行透视变换矫正
    cv::Mat correctedFront = correctPerspective(m_originFront, frontResult.corners);
    cv::Mat correctedBack = correctPerspective(m_originBack, backResult.corners);

    // 确保正反面尺寸一致（按比例缩放）
    float scale = std::min(
        static_cast<float>(correctedFront.cols) / correctedBack.cols,
        static_cast<float>(correctedFront.rows) / correctedBack.rows
        );
    cv::resize(correctedBack, correctedBack, cv::Size(), scale, scale);

    // 创建垂直排列的白色背景图像（添加适当间距）
    int spacing = 100; // 正反面之间的间距
    int totalWidth = correctedFront.cols;
    int totalHeight = correctedFront.rows + correctedBack.rows + spacing;

    cv::Mat combined(totalHeight, totalWidth, CV_8UC3, cv::Scalar(255, 255, 255));

    // 将矫正后的正反面图像复制到合成图像
    cv::Mat frontROI(combined, cv::Rect(0, 0, correctedFront.cols, correctedFront.rows));
    correctedFront.copyTo(frontROI);

    cv::Mat backROI(combined, cv::Rect(0, correctedFront.rows + spacing,
                                       correctedBack.cols, correctedBack.rows));
    correctedBack.copyTo(backROI);

    // 保存为预览图
    m_preview = cvMatToQPixmap(combined);

    // 显示预览图
    ui->PreviewOutput->setPixmap(m_preview.scaled(
        ui->PreviewOutput->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation
        ));
}

//将正面和反面的标注可视化图像显示在ui中
void CardDetectDlg::visualize(const cv::Mat &fvisualizedImage, const cv::Mat &bvisualizedImage)
{
    // 显示正面标注图
    QPixmap frontPix = cvMatToQPixmap(fvisualizedImage);
    ui->FrontImage->setPixmap(frontPix.scaled(
        ui->FrontImage->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation
        ));

    // 显示反面标注图
    QPixmap backPix = cvMatToQPixmap(bvisualizedImage);
    ui->BackImage->setPixmap(backPix.scaled(
        ui->BackImage->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation
        ));
}

//将预览图m_preview导出为图片保存(文件路径选择窗口)
void CardDetectDlg::on_ExportBtn_clicked()
{
    if (m_preview.isNull()) {
        QMessageBox::warning(this, "导出失败", "没有可导出的预览图像");
        return;
    }

    // 打开文件保存对话框
    QString filePath = QFileDialog::getSaveFileName(
        this, "保存扫描结果", "", "图像文件 (*.png *.jpg *.jpeg *.bmp)"
        );

    if (!filePath.isEmpty()) {
        // 保存图像
        if (m_preview.save(filePath)) {
            QMessageBox::information(this, "导出成功", "图像已成功保存");
        } else {
            QMessageBox::warning(this, "导出失败", "无法保存图像文件");
        }
    }
}

cv::Mat CardDetectDlg::correctPerspective(const cv::Mat &image, const std::vector<cv::Point2f> &corners)
{
    // 确保有4个角点
    if (corners.size() != 4) {
        return image.clone();
    }

    // 计算目标宽度和高度（根据实际证件比例，这里假设为身份证比例约54:85.6）
    cv::Point2f p0 = corners[0];
    cv::Point2f p1 = corners[1];
    cv::Point2f p2 = corners[2];
    cv::Point2f p3 = corners[3];

    // 计算原始宽度和高度
    float w1 = cv::norm(p1 - p0);
    float w2 = cv::norm(p2 - p3);
    float h1 = cv::norm(p3 - p0);
    float h2 = cv::norm(p2 - p1);
    float maxWidth = std::max(w1, w2);
    float maxHeight = std::max(h1, h2);

    // 目标点（按顺序对应输入的角点）
    std::vector<cv::Point2f> dstPoints;
    dstPoints.emplace_back(0, 0);
    dstPoints.emplace_back(maxWidth - 1, 0);
    dstPoints.emplace_back(maxWidth - 1, maxHeight - 1);
    dstPoints.emplace_back(0, maxHeight - 1);

    // 计算透视变换矩阵并应用
    cv::Mat M = cv::getPerspectiveTransform(corners, dstPoints);
    cv::Mat corrected;
    cv::warpPerspective(image, corrected, M, cv::Size(maxWidth, maxHeight));

    return corrected;
}

void CardDetectDlg::triggerDetection()
{
    // 1. 双条件校验：模型未加载 或 图片路径未就绪，直接返回
    if (!m_isModelLoaded || !m_pathsReady) {
        qDebug() << "[triggerDetection] 条件不满足（模型加载："
                 << m_isModelLoaded << "，图片就绪：" << m_pathsReady << "），不触发检测";
        return;
    }

    // 2. 加载图片（从已保存的路径读取）
    m_originFront = cv::imread(m_frontPath.toStdString());
    m_originBack = cv::imread(m_backPath.toStdString());

    // 3. 校验图片是否成功读取（避免格式不支持、路径错误等问题）
    if (m_originFront.empty()) {
        QMessageBox::warning(this, "图片错误", "无法读取正面图片（格式不支持或文件损坏）：" + m_frontPath);
        return;
    }
    if (m_originBack.empty()) {
        QMessageBox::warning(this, "图片错误", "无法读取反面图片（格式不支持或文件损坏）：" + m_backPath);
        return;
    }

    // 4. 双条件均满足且图片有效，触发检测
    qDebug() << "[triggerDetection] 模型加载完成 + 图片就绪，发射检测信号";
    emit m_detector->sig_detect(m_originFront, m_originBack);
}

DetectorThread::DetectorThread(CvUtils::cardCornerDetector *detector)
{
    m_detectThread = new QThread();
    if (!m_detectThread) {
        qCritical() << "[DetectorThread] 创建QThread失败！";
        return;
    }

    // 移动检测器到线程（必须在线程启动前）
    detector->moveToThread(m_detectThread);

    // 线程结束时自动删除线程对象
    CardDetectDlg::connect(m_detectThread, &QThread::finished, m_detectThread, &QObject::deleteLater);

    // 启动线程
    m_detectThread->start();
}

DetectorThread::~DetectorThread()
{
    m_detectThread->quit();
    m_detectThread->wait(); // 等待线程安全退出
}
